package org.ngmj.client.rpc.proxy;

import io.netty.channel.Channel;
import io.netty.util.concurrent.DefaultPromise;
import org.ngmj.client.rpc.SequenceIdGenerator;
import org.ngmj.message.RpcRequestMessage;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import static org.ngmj.client.handler.RpcResponseMessageHandler.PROMISES_MAP;

public class ProxyHandler implements InvocationHandler {
    private final Class<?> interfaceClass;
    private final Channel channel;

    public <T> ProxyHandler(Class<T> interfaceClass, Channel channel) {
        this.interfaceClass = interfaceClass;
        this.channel = channel;
    }

    public static <T> T newProxyInstance(Class<T> interfaceClass, Channel channel) {
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, new ProxyHandler(interfaceClass, channel));
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcRequestMessage rpcRequestMessage = new RpcRequestMessage(
                SequenceIdGenerator.getNextId(),
                interfaceClass.getName(),
                method.getName(),
                method.getReturnType(), method.getParameterTypes(), args);
        DefaultPromise<Object> promise = new DefaultPromise<>(channel.eventLoop());
        PROMISES_MAP.put(rpcRequestMessage.getSequenceId(), promise);
        channel.writeAndFlush(rpcRequestMessage);
        promise.await();

        if (promise.isSuccess()) {
            return promise.getNow();
        } else {
            throw new RuntimeException(promise.cause());
        }
    }
}
